1   /*
2    * Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved.
3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4    *
5    * This code is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU General Public License version 2 only, as
7    * published by the Free Software Foundation.
8    *
9    * This code is distributed in the hope that it will be useful, but WITHOUT
10   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12   * version 2 for more details (a copy is included in the LICENSE file that
13   * accompanied this code).
14   *
15   * You should have received a copy of the GNU General Public License version
16   * 2 along with this work; if not, write to the Free Software Foundation,
17   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18   *
19   * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20   * or visit www.oracle.com if you need additional information or have any
21   * questions.
22   */
23  
24  /*
25   * @test
26   * @bug 4902952 4905407 4916149
27   * @summary Tests that the scale of zero is propagated properly and has the proper effect.
28   * @author Joseph D. Darcy
29   */
30  
31  import java.math.*;
32  import java.util.*;
33  
34  public class ZeroScalingTests {
35  
36      static MathContext longEnough = new MathContext(50, RoundingMode.UNNECESSARY);
37  
38      static BigDecimal[]  zeros = new BigDecimal[23];
39      static {
40          for(int i = 0; i < 21; i++) {
41              zeros[i] = new BigDecimal(BigInteger.ZERO, i-10);
42          }
43          zeros[21] = new BigDecimal(BigInteger.ZERO, Integer.MIN_VALUE);
44          zeros[22] = new BigDecimal(BigInteger.ZERO, Integer.MAX_VALUE);
45      }
46  
47      static BigDecimal element = BigDecimal.valueOf(100, -2);
48  
49      static MathContext contexts[] = {
50          new MathContext(0, RoundingMode.UNNECESSARY),
51          new MathContext(100, RoundingMode.UNNECESSARY),
52          new MathContext(5, RoundingMode.UNNECESSARY),
53          new MathContext(4, RoundingMode.UNNECESSARY),
54          new MathContext(3, RoundingMode.UNNECESSARY),
55          new MathContext(2, RoundingMode.UNNECESSARY),
56          new MathContext(1, RoundingMode.UNNECESSARY),
57      };
58  
59  
60      static int addTests() {
61          int failures = 0;
62  
63          for(BigDecimal zero1: zeros) {
64              for(BigDecimal zero2: zeros) {
65                  BigDecimal expected = new BigDecimal(BigInteger.ZERO,
66                                                       Math.max(zero1.scale(), zero2.scale()));
67                  BigDecimal result;
68  
69                  if(! (result=zero1.add(zero2)).equals(expected) ) {
70                      failures++;
71                      System.err.println("For classic exact add, expected scale of " +
72                                         expected.scale() + "; got " +
73                                         result.scale() + ".");
74                  }
75  
76                  if(! (result=zero1.add(zero2, MathContext.UNLIMITED)).equals(expected) ) {
77                      failures++;
78                      System.err.println("For UNLIMITED math context add," +
79                                         " expected scale of " +
80                                         expected.scale() + "; got " +
81                                         result.scale() + ".");
82                  }
83  
84                  if(! (result=zero1.add(zero2, longEnough)).equals(expected) ) {
85                      failures++;
86                      System.err.println("For longEnough math context add," +
87                                         " expected scale of " +
88                                         expected.scale() + "; got " +
89                                         result.scale() + ".");
90                  }
91  
92              }
93          }
94  
95          // Test effect of adding zero to a nonzero value.
96          for (MathContext mc: contexts) {
97              for (BigDecimal zero: zeros) {
98                  if (Math.abs((long)zero.scale()) < 100 ) {
99  
100                     int preferredScale = Math.max(zero.scale(), element.scale());
101                     if (mc.getPrecision() != 0) {
102                         if (preferredScale < -4 )
103                             preferredScale = -4;
104                         else if (preferredScale > -(5 - mc.getPrecision())) {
105                             preferredScale = -(5 - mc.getPrecision());
106                         }
107                     }
108 
109 
110                     /*
111                       System.err.println("\n    " + element + " +\t" + zero + " =\t" + result);
112 
113                       System.err.println("scales" + element.scale() + " \t" + zero.scale() +
114                       "  \t " + result.scale() + "\t precison = " + mc.getPrecision());
115                       System.err.println("expected scale = " + preferredScale);
116                     */
117 
118                     BigDecimal result = element.add(zero, mc);
119                     if (result.scale() != preferredScale ||
120                             result.compareTo(element) != 0) {
121                         failures++;
122                         System.err.println("Expected scale  " + preferredScale +
123                                            " result scale was " + result.scale() +
124                                            " ; value was " + result);
125                     }
126 
127                     result = zero.add(element, mc);
128                     if (result.scale() != preferredScale ||
129                             result.compareTo(element) != 0) {
130                         failures++;
131                         System.err.println("Expected scale  " + preferredScale +
132                                            " result scale was " + result.scale() +
133                                            " ; value was " + result);
134                     }
135 
136                     result = element.negate().add(zero, mc);
137                     if (result.scale() != preferredScale ||
138                             result.compareTo(element.negate()) != 0) {
139                         failures++;
140                         System.err.println("Expected scale  " + preferredScale +
141                                            " result scale was " + result.scale() +
142                                            " ; value was " + result);
143                     }
144 
145                     result = zero.add(element.negate(), mc);
146                     if (result.scale() != preferredScale ||
147                             result.compareTo(element.negate()) != 0) {
148                         failures++;
149                         System.err.println("Expected scale  " + preferredScale +
150                                            " result scale was " + result.scale() +
151                                            " ; value was " + result);
152                     }
153 
154                 }
155             }
156         }
157 
158         return failures;
159     }
160 
161     static int subtractTests() {
162         int failures = 0;
163 
164         for(BigDecimal zero1: zeros) {
165             for(BigDecimal zero2: zeros) {
166                 BigDecimal expected = new BigDecimal(BigInteger.ZERO,
167                                                      Math.max(zero1.scale(), zero2.scale()));
168                 BigDecimal result;
169 
170                 if(! (result=zero1.subtract(zero2)).equals(expected) ) {
171                     failures++;
172                     System.err.println("For classic exact subtract, expected scale of " +
173                                        expected.scale() + "; got " +
174                                        result.scale() + ".");
175                 }
176 
177                 if(! (result=zero1.subtract(zero2, MathContext.UNLIMITED)).equals(expected) ) {
178                     failures++;
179                     System.err.println("For UNLIMITED math context subtract," +
180                                        " expected scale of " +
181                                        expected.scale() + "; got " +
182                                        result.scale() + ".");
183                 }
184 
185                 if(! (result=zero1.subtract(zero2, longEnough)).equals(expected) ) {
186                     failures++;
187                     System.err.println("For longEnough math context subtract," +
188                                        " expected scale of " +
189                                        expected.scale() + "; got " +
190                                        result.scale() + ".");
191                 }
192 
193             }
194         }
195 
196 
197         // Test effect of adding zero to a nonzero value.
198         for (MathContext mc: contexts) {
199             for (BigDecimal zero: zeros) {
200                 if (Math.abs((long)zero.scale()) < 100 ) {
201 
202                     int preferredScale = Math.max(zero.scale(), element.scale());
203                     if (mc.getPrecision() != 0) {
204                         if (preferredScale < -4 )
205                             preferredScale = -4;
206                         else if (preferredScale > -(5 - mc.getPrecision())) {
207                             preferredScale = -(5 - mc.getPrecision());
208                         }
209                     }
210 
211 
212                     /*
213                       System.err.println("\n    " + element + " +\t" + zero + " =\t" + result);
214 
215                       System.err.println("scales" + element.scale() + " \t" + zero.scale() +
216                       "  \t " + result.scale() + "\t precison = " + mc.getPrecision());
217                       System.err.println("expected scale = " + preferredScale);
218                     */
219 
220                     BigDecimal result = element.subtract(zero, mc);
221                     if (result.scale() != preferredScale ||
222                             result.compareTo(element) != 0) {
223                         failures++;
224                         System.err.println("Expected scale  " + preferredScale +
225                                            " result scale was " + result.scale() +
226                                            " ; value was " + result);
227                     }
228 
229                     result = zero.subtract(element, mc);
230                     if (result.scale() != preferredScale ||
231                             result.compareTo(element.negate()) != 0) {
232                         failures++;
233                         System.err.println("Expected scale  " + preferredScale +
234                                            " result scale was " + result.scale() +
235                                            " ; value was " + result);
236                     }
237 
238                     result = element.negate().subtract(zero, mc);
239                     if (result.scale() != preferredScale ||
240                             result.compareTo(element.negate()) != 0) {
241                         failures++;
242                         System.err.println("Expected scale  " + preferredScale +
243                                            " result scale was " + result.scale() +
244                                            " ; value was " + result);
245                     }
246 
247                     result = zero.subtract(element.negate(), mc);
248                     if (result.scale() != preferredScale ||
249                             result.compareTo(element) != 0) {
250                         failures++;
251                         System.err.println("Expected scale  " + preferredScale +
252                                            " result scale was " + result.scale() +
253                                            " ; value was " + result);
254                     }
255 
256                 }
257             }
258         }
259 
260         return failures;
261     }
262 
263     static int multiplyTests() {
264         int failures = 0;
265 
266         BigDecimal ones[] = {
267             BigDecimal.valueOf(1, 0),
268             BigDecimal.valueOf(10, 1),
269             BigDecimal.valueOf(1000, 3),
270             BigDecimal.valueOf(100000000, 8),
271         };
272 
273         List<BigDecimal> values = new LinkedList<BigDecimal>();
274         values.addAll(Arrays.asList(zeros));
275         values.addAll(Arrays.asList(ones));
276 
277         for(BigDecimal zero1: zeros) {
278             for(BigDecimal value: values) {
279                 BigDecimal expected = new BigDecimal(BigInteger.ZERO,
280                                                      (int)Math.min(Math.max((long)zero1.scale()+value.scale(),
281                                                                             Integer.MIN_VALUE ),
282                                                                    Integer.MAX_VALUE ) );
283                 BigDecimal result;
284 
285                 if(! (result=zero1.multiply(value)).equals(expected) ) {
286                     failures++;
287                     System.err.println("For classic exact multiply, expected scale of " +
288                                        expected.scale() + "; got " +
289                                        result.scale() + ".");
290                 }
291 
292                 if(! (result=zero1.multiply(value, MathContext.UNLIMITED)).equals(expected) ) {
293                     failures++;
294                     System.err.println("For UNLIMITED math context multiply," +
295                                        " expected scale of " +
296                                        expected.scale() + "; got " +
297                                        result.scale() + ".");
298                 }
299 
300                 if(! (result=zero1.multiply(value, longEnough)).equals(expected) ) {
301                     failures++;
302                     System.err.println("For longEnough math context multiply," +
303                                        " expected scale of " +
304                                        expected.scale() + "; got " +
305                                        result.scale() + ".");
306                 }
307 
308             }
309         }
310 
311         return failures;
312     }
313 
314     static int divideTests() {
315         int failures = 0;
316 
317         BigDecimal [] ones = {
318             BigDecimal.valueOf(1, 0),
319             BigDecimal.valueOf(10, -1),
320             BigDecimal.valueOf(100, -2),
321             BigDecimal.valueOf(1000, -3),
322             BigDecimal.valueOf(1000000, -5),
323         };
324 
325         for(BigDecimal one: ones) {
326             for(BigDecimal zero: zeros) {
327                 BigDecimal expected = new BigDecimal(BigInteger.ZERO,
328                                                      (int)Math.min(Math.max((long)zero.scale() - one.scale(),
329                                                                             Integer.MIN_VALUE ),
330                                                                    Integer.MAX_VALUE ) );
331                 BigDecimal result;
332 
333                 if(! (result=zero.divide(one)).equals(expected) ) {
334                     failures++;
335                     System.err.println("For classic exact divide, expected scale of " +
336                                        expected.scale() + "; got " +
337                                        result.scale() + ".");
338                 }
339 
340                 if(! (result=zero.divide(one, MathContext.UNLIMITED)).equals(expected) ) {
341                     failures++;
342                     System.err.println("For UNLIMITED math context divide," +
343                                        " expected scale of " +
344                                        expected.scale() + "; got " +
345                                        result.scale() + ".");
346                 }
347 
348                 if(! (result=zero.divide(one, longEnough)).equals(expected) ) {
349                     failures++;
350                     System.err.println("For longEnough math context divide," +
351                                        " expected scale of " +
352                                        expected.scale() + "; got " +
353                                        result.scale() + ".");
354                 }
355 
356             }
357         }
358 
359         return failures;
360     }
361 
362     static int setScaleTests() {
363         int failures = 0;
364 
365         int scales[] = {
366             Integer.MIN_VALUE,
367             Integer.MIN_VALUE+1,
368             -10000000,
369             -3,
370             -2,
371             -1,
372             0,
373             1,
374             2,
375             3,
376             10,
377             10000000,
378             Integer.MAX_VALUE-1,
379             Integer.MAX_VALUE
380         };
381 
382         for(BigDecimal zero: zeros) {
383             for(int scale: scales) {
384                 try {
385                     BigDecimal bd = zero.setScale(scale);
386                 }
387                 catch (ArithmeticException e) {
388                     failures++;
389                     System.err.println("Exception when trying to set a scale of " + scale +
390                                        " on " + zero);
391                 }
392             }
393         }
394 
395         return failures;
396     }
397 
398     static int toEngineeringStringTests() {
399         int failures = 0;
400 
401         String [][] testCases  = {
402             {"0E+10",   "0.00E+12"},
403             {"0E+9",    "0E+9"},
404             {"0E+8",    "0.0E+9"},
405             {"0E+7",    "0.00E+9"},
406 
407             {"0E-10",   "0.0E-9"},
408             {"0E-9",    "0E-9"},
409             {"0E-8",    "0.00E-6"},
410             {"0E-7",    "0.0E-6"},
411         };
412 
413         for(String[] testCase: testCases) {
414             BigDecimal bd = new BigDecimal(testCase[0]);
415             String result = bd.toEngineeringString();
416 
417             if (!result.equals(testCase[1]) ||
418                 !bd.equals(new BigDecimal(result))) {
419                 failures++;
420                 System.err.println("From input ``" + testCase[0] + ",'' " +
421                                    " bad engineering string output ``" + result +
422                                    "''; expected ``" + testCase[1] + ".''");
423             }
424 
425         }
426 
427         return failures;
428     }
429 
430     static int ulpTests() {
431         int failures = 0;
432 
433         for(BigDecimal zero: zeros) {
434             BigDecimal result;
435             BigDecimal expected = BigDecimal.valueOf(1, zero.scale());
436 
437             if (! (result=zero.ulp()).equals(expected) ) {
438                 failures++;
439                 System.err.println("Unexpected ulp value for zero value " +
440                                    zero + "; expected " + expected +
441                                    ", got " + result);
442             }
443         }
444 
445         return failures;
446     }
447 
448     public static void main(String argv[]) {
449         int failures = 0;
450 
451         failures += addTests();
452         failures += subtractTests();
453         failures += multiplyTests();
454         failures += divideTests();
455         failures += setScaleTests();
456         failures += toEngineeringStringTests();
457         failures += ulpTests();
458 
459         if (failures > 0 ) {
460             throw new RuntimeException("Incurred " + failures + " failures" +
461                                        " testing the preservation of zero scales.");
462         }
463     }
464 }